package com.jie.component.lock.aspect;

import com.jie.component.exception.base.BusinessException;
import com.jie.component.lock.annotion.RepeatExecuteLimit;
import com.jie.component.lock.core.LockType;
import com.jie.component.lock.core.ManageLocker;
import com.jie.component.lock.factory.ServiceLockFactory;
import com.jie.component.lock.locker.ServiceLocker;
import com.jie.component.lock.lockinfo.LockInfoHandle;
import com.jie.component.lock.lockinfo.LockInfoType;
import com.jie.component.lock.lockinfo.factory.LockInfoHandleFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author jie
 * @version 1.0 2025-02-12
 */
@Slf4j
@Aspect
@RequiredArgsConstructor
@Order(-11) // 让锁的切面在事务的切面之前执行
public class RepeatExecuteLimitAspect {

    private static final String PREFIX_NAME = "repeat_flag";

    private static final String SUCCESS_FLAG = "success";

    private final ManageLocker manageLocker;

    private final ServiceLockFactory serviceLockFactory;

    private final StringRedisTemplate stringRedisTemplate;

    private final LockInfoHandleFactory lockInfoHandleFactory;

    @Around("@annotation(repeatExecuteLimit)")
    public Object around(ProceedingJoinPoint joinPoint, RepeatExecuteLimit repeatExecuteLimit) throws Throwable {
        long durationTime = repeatExecuteLimit.durationTime();
        String message = repeatExecuteLimit.message();
        // 返回值
        Object obj;

        // 获取锁的信息
        LockInfoHandle lockInfoHandle = lockInfoHandleFactory.getLockInfoHandle(LockInfoType.REPEAT_EXECUTE_LIMIT);
        String lockName = lockInfoHandle.getLockName(joinPoint,repeatExecuteLimit.name(), repeatExecuteLimit.keys());

        // 获取幂等标识
        String repeatFlagName = PREFIX_NAME + lockName;
        String flagObject = stringRedisTemplate.opsForValue().get(repeatFlagName);
        if (SUCCESS_FLAG.equals(flagObject)) {
            // 抛出异常
            throw new BusinessException(message);
        }

        // 获取本地锁
        ReentrantLock localLock = manageLocker.getLocalLock(lockName, true);
        boolean isLock = localLock.tryLock();
        if (!isLock) {
            throw new BusinessException(message);
        }

        // 获取分布式锁
        try {
            ServiceLocker lock = serviceLockFactory.getLock(LockType.Fair);
            isLock = lock.tryLock(lockName, TimeUnit.SECONDS, 0);

            if (!isLock) {
                throw new BusinessException(message);
            }

            try {
                // 再次获取幂等标识
                flagObject = stringRedisTemplate.opsForValue().get(repeatFlagName);
                if (SUCCESS_FLAG.equals(flagObject)) {
                    throw new BusinessException(message);
                }

                obj = joinPoint.proceed();
                if (durationTime > 0) {
                    // 设置幂等标识
                    stringRedisTemplate.opsForValue().set(repeatFlagName, SUCCESS_FLAG, durationTime, TimeUnit.SECONDS);
                }
                return obj;
            } finally {
                // 释放分布式锁
                lock.unlock(lockName);
            }
        } finally {
            // 释放本地锁
            localLock.unlock();
        }
    }

}
